package net.cyndeline.entitySystem.defaultImpl

import java.util.BitSet
import scala.collection.mutable.HashSet
import scala.collection.mutable.Map
import scala.collection.mutable.MutableList
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
import net.cyndeline.entitySystem.Component
import net.cyndeline.entitySystem.ComponentTable
import net.cyndeline.misc.Bag
import net.cyndeline.entitySystem.EntityRef

class DefaultComponentTable extends ComponentTable {
  private var store = Map[Class[_], Map[Int, Component]]()
  
  /* The index each component class is mapped to. */
  private var nextAvailableIndex = 0
  private var componentIndex = Map[Class[_ <: Component], Int]()
  
  /* Stores which components a particular entity has set.
   * The index in the bag corresponds to the entity id. 
   */
  private var compBits = new Bag[BitSet]()
  
  /* The top bag stores each set of components based on its
   * component index. The subsequent bag contains all components
   * of a specific class, with the components position in the bag
   * corresponding to the entity's id.
   */
  private var components = new Bag[Bag[Component]]()
  
  override def get[A <: Component](entityId: Int, componentClass: Class[A]): Option[Component] = {
    val index = componentIndex.get(componentClass)
    var componentBag: Bag[Component] = null
    
    if (index.isDefined) {
      componentBag = components.get(index.get).get
      componentBag.get(entityId)
    } else {
      None
    }
    
//    val entityMap = store get componentClass
//    val componentMap = entityMap getOrElse {
//      return None
//    }
//    
//    componentMap get entityId
  }

  override def put(entityId: Int, component: Component): Option[Component] = {
    val index = componentIndex.get(component.getClass)
    var componentBag: Bag[Component] = null
    var entityBitSet: Option[BitSet] = compBits.get(entityId)
    var bitIndex: Int = 0
    var previousExisted = false
    var oldComponent: Option[Component] = None
    
    if (!entityBitSet.isDefined) {
      entityBitSet = Option(new BitSet())
      compBits.set(entityId, entityBitSet.get)
    }
    
    if (index.isDefined) {
      var presentIndex = index.get
      componentBag = components.get(presentIndex).get
      
      /* Check if there's no previous component of the same type. */
      oldComponent = componentBag.get(entityId)
      if (oldComponent.isDefined) {
        previousExisted = true
      }
      
      componentBag.set(entityId, component)
      bitIndex = presentIndex
    } else {
      
      /* Create the bag that should hold every component of this new type. */
      val newBag = new Bag[Component]()
      newBag.set(entityId, component)
      
      /* Assign it an index. */
      bitIndex = nextAvailableIndex
      componentIndex.put(component.getClass, bitIndex)
      components.set(bitIndex, newBag)
      nextAvailableIndex += 1
    }
    
    /* Set the bit marking that the entity possesses the component. */
    entityBitSet.get.set(bitIndex)
    
    if (previousExisted) {
      oldComponent
    } else {
      None
    }

//    val entityMap = store get component.getClass
//    var previousExisted = false
//    val componentMap = entityMap getOrElse Map()
//    
//    /* Check if this id was mapped to a previous component of the same type. */
//    if (componentMap contains entityId) {
//      previousExisted = true
//    }
//    
//    store = (store + (component.getClass -> (componentMap + (entityId -> component))))
//    
//    if (previousExisted) {
//      Option(component)
//    } else {
//      None
//    }
  }

  override def remove[T <: Component](entityId: Int, componentClass: Class[T]): Option[T] = {
    val index = componentIndex.get(componentClass)
    var componentBag: Bag[Component] = null
    var entityBitSet: Option[BitSet] = compBits.get(entityId)
    
    if (index.isDefined) {
      var defIndex = index.get
      componentBag = components.get(defIndex).get
      val component = componentBag.get(entityId)
      
      /* Clear it from the bag. */
      componentBag.set(entityId, null)
      
      /* Clear the bit. If no more bits present, that means this entity
       * no longer is present in the table.
       */
      if (entityBitSet.isDefined) {
        var bits = entityBitSet.get
        bits.clear(defIndex)
        
        if (bits.isEmpty()) {
          compBits.set(entityId, null)
        }
      }
      
      if (component.isDefined) {
        Option(componentClass.cast(component getOrElse None))
      } else {
        None
      }
    } else {
      None
    }
//    val entityMap = store get componentClass
//    val componentMap = entityMap getOrElse Map()
//    if (componentMap contains entityId) {
//      store += componentClass -> (componentMap - entityId)
//      val result = componentMap get entityId
//      Option(componentClass.cast(result getOrElse None))
//    } else {
//      None
//    }
  }
  
  override def remove(entityId: Int): Unit = {
    if (entityId == EntityRef.NULL.id) {
      throw new IllegalArgumentException("cannot remove components from null id")
    }
    var b = compBits.get(entityId)
    
    if (b.isDefined) {
      var bits = b.get
      
      var i = bits.nextSetBit(0) // -1 when no more set bits
      while (i >= 0) {
        val compBag: Bag[Component] = components.get(i).get
        compBag.remove(entityId)
        i = bits.nextSetBit(i + 1)
      }
      
      compBits.remove(entityId)
    }
    
//    val allKeys = store.keys
//    
//    for (componentKey <- allKeys) {
//      val currentMap = store(componentKey)
//      store = store + (componentKey -> (currentMap - entityId))
//    }
  }
  
  override def clear() {
    nextAvailableIndex = 0
    componentIndex = Map[Class[_ <: Component], Int]()
    compBits = new Bag[BitSet]()
    components = new Bag[Bag[Component]]()
    //store = store.empty
  }

  override def getComponentCount(componentClass: Class[_ <: Component]): Int = {
    val index = componentIndex.get(componentClass)
    var componentBag: Bag[Component] = null
    
    if (index.isDefined) {
      componentBag = components.get(index.get).get
      componentBag.size
    } else {
      0
    }
//    val entityMap = store get componentClass
//    val componentMap = entityMap getOrElse Map()
//    componentMap size
  }

  override def iterateComponents(entityId: Int): Iterator[Component] = {
    if (entityId == EntityRef.NULL.id) {
      throw new IllegalArgumentException("Null entities have no component iterator")
    }
    
    var result = ListBuffer[Component]()    
    var b = compBits.get(entityId)
    
    if (b.isDefined) {
      var bits = b.get
      
      var i = bits.nextSetBit(0) // -1 when no more set bits
      while (i >= 0) {
        val compBag: Bag[Component] = components.get(i).get
        result += compBag.get(entityId).get
        i = bits.nextSetBit(i + 1)
      }
    }
    
    result.iterator
    
//    val result: MutableList[Component] = MutableList()
//    val allMaps = store values
//    
//    for (componentMap <- allMaps) {
//      (componentMap get entityId) match {
//        case Some(component) => result += component
//        case _ => {}
//      }
//    }
//      
//    result.iterator
  }

//  override def componentMappingss[T <: Component](componentClass: Class[T]): Option[Map[Int, Component]] = {
//    var index = componentIndex.get(componentClass)
//    
//    if (index.isDefined) {
//      
//      /* Every component belonging to the specified class. */
//      val componentBag: Bag[Component] = components.get(index.get).get
//      var i = 0
//      while (i < componentBag.size) {
//        
//        i += 1
//      }
//    }
//    
//    var map = Map[Int, Component]()
//    None
////    val entityMap = store get componentClass
////    entityMap
//  }

//  override def entityIdIteratorss: Iterator[Int] = {
//    var result = HashSet[Int]()
//    for (componentMap <- (store values)) {
//      result = result ++ componentMap.keys
//    }
//    
//    result.iterator
//  }
  
  override def numEntities: Int = {
    compBits.size
  }
  
}